package io.renren.commons.dynamic.datasource.aspect;

import io.renren.common.utils.R;
import io.renren.commons.dynamic.datasource.annotation.TransactionDataSource;
import io.renren.commons.dynamic.datasource.config.DynamicConnectionHolder;
import io.renren.commons.dynamic.datasource.config.DynamicContextHolder;
import io.renren.commons.dynamic.datasource.config.DynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Deque;

/**
 * 自定义事务处理切面
 *
 * @author hou 2019-5-28 20:43:36
 */
@Aspect
@Component
public class DataTransactionAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private DynamicDataSource dynamicDataSource;

    /**
     * 事务控制切点
     */
    @Pointcut("@annotation(io.renren.commons.dynamic.datasource.annotation.DataTransaction) ")
    public void dataTransactionPointCut() {

    }

    @Pointcut("@annotation(io.renren.commons.dynamic.datasource.annotation.TransactionDataSource) ")
    public void dataSourcePointCut() {

    }

    @Before("dataSourcePointCut()")
    public void before(JoinPoint point) throws SQLException {
        logger.info("data open transaction before carry out method:{}", point.getSignature().getName());
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class targetClass = point.getTarget().getClass();
        Method method = signature.getMethod();
        TransactionDataSource targetDataSource = (TransactionDataSource) targetClass.getAnnotation(TransactionDataSource.class);
        TransactionDataSource methodDataSource = method.getAnnotation(TransactionDataSource.class);
        if (targetDataSource != null || methodDataSource != null) {
            String value;
            if (methodDataSource != null) {
                value = methodDataSource.value();
            } else {
                value = targetDataSource.value();
            }
            DynamicContextHolder.push(value);
            logger.debug("data open transaction before set datasource is {}", value);
            Connection connection = dynamicDataSource.getConnection();
            DynamicConnectionHolder.push(connection);
        }
    }

    /**
     * 正常执行提交数据并响应
     *
     * @return result
     * @throws SQLException e
     */
    @AfterReturning(value = "dataTransactionPointCut()", returning = "result")
    public Object afterReturning(JoinPoint point, Object result) throws SQLException {
        logger.info("data commit after returning carry out method:{}", point.getSignature().getName());
        Deque<Connection> deque = DynamicConnectionHolder.getConnectionDeque();
        final int size = deque.size();
        for (int i = 0; i < size; i++) {
            Connection connection = deque.removeFirst();
            connection.commit();
            connection.close();
            logger.info("该数据源数据提交:{}", connection);
        }
        DynamicContextHolder.poll();
        DynamicConnectionHolder.poll();
        logger.info("my clean datasource");
        return result;
    }

    /**
     * 发生异常时回滚并且做出响应
     *
     * @param point point
     * @param e     e
     * @return result
     * @throws SQLException e
     */
    @AfterThrowing(value = "dataTransactionPointCut()", throwing = "e")
    public Object afterThrowing(JoinPoint point, Exception e) throws SQLException {
        logger.info("data rollback after throwing carry out method:{}", point.getSignature().getName());
        logger.error(e.getMessage(), e);
        Deque<Connection> deque = DynamicConnectionHolder.getConnectionDeque();
        final int size = deque.size();
        for (int i = 0; i < size; i++) {
            Connection connection = deque.removeFirst();
            connection.rollback();
            connection.close();
            logger.info("该数据源数据回滚:{}", connection);
        }
        DynamicContextHolder.poll();
        DynamicConnectionHolder.poll();
        logger.info("my clean datasource");
        return R.error();
    }

}
